﻿using System;
using System.Collections.Generic;
using FunctionalCLR.Core;
using FunctionalCLR.Core.Function.Arithmetics;
using FunctionalCLR.Core.Functions.Maths;
using FunctionalCLR.Shell.Function;
using Zove.Runtime;
using Zove.Runtime.FunctionalCLR;

namespace Zove.Compiler.EquationTransformer {
    public class DefaultEquationTransformer : IEquationTransformer {

        private string result;

        private string expr;

        private List<string> DuplicatedIdentifiers {
            get;
            set;
        }

        public Dictionary<string, Node> Nodes {
            get;
            set;
        }

        public static List<string> CheckDuplicated(string equ) {
            List<string> id = new List<string>();
            List<string> dup = new List<string>();
            Lexer lexer = new Lexer(equ);

            while (lexer.HasMoreTokens()) {
                Token token = lexer.NextToken();
                if (token.TokenType == TokenType.Identifier) {
                    if (id.Contains(token.Value)) {
                        dup.Add(token.Value);
                    } else {
                        id.Add(token.Value);
                    }
                }
            }

            return dup;
        }

        private RuntimeModel rm = null;

        public DefaultEquationTransformer() {
        }

        public DefaultEquationTransformer(string equ, RuntimeModel rm)
            : this() {
            if (equ.IndexOf('=') < 0) {
                throw new ArgumentException("equation should have = mark");
            }

            this.DuplicatedIdentifiers = CheckDuplicated(equ);
            this.result = equ.Split('=')[0].Trim();
            this.expr = equ.Split('=')[1].Trim();
            this.Nodes = new Dictionary<string, Node>();
            this.rm = rm;

            this.TransformAll();
        }

        public Node this[string elementName] {
            get {
                return this.Nodes[elementName];
            }
        }

        public Dictionary<string, string> Transform() {
            Dictionary<string, string> exprColl
                = new Dictionary<string, string>();

            foreach (string m in this.Nodes.Keys) {
                Node n = this.Nodes[m];
                string ex =
                    ZoveDependencyManager.Instance.ExpressionCompiler.Compile(n);

                exprColl[m] = ex;
            }

            return exprColl;
        }

        public Node LeftTree {
            get;
            set;
        }

        public Node RightTree {
            get;
            set;
        }

        /// <summary>
        /// Transforming the expressions
        /// </summary>
        public void TransformAll() {
            //exprColl[this.result] = this.expr;
            Parser[] p = new Parser[2];

            p[0] = new ZoomParser(this.result, this.rm);
            p[0].ConstantManager
                = ZoveDependencyManager.Instance.ConstantManager;
            p[0].FunctionManager
                = ZoveDependencyManager.Instance.FunctionManager;

            p[1] = new ZoomParser(this.expr, this.rm);
            p[1].ConstantManager
                = ZoveDependencyManager.Instance.ConstantManager;
            p[1].FunctionManager
                = ZoveDependencyManager.Instance.FunctionManager;

            Node[] roots = new Node[] { p[0].Parse(), p[1].Parse() };
            this.LeftTree = roots[0];
            this.RightTree = roots[1];

            for (int index = 0; index < 2; index++) {
                foreach (string m in p[index].Identifiers) {
                    if (this.DuplicatedIdentifiers.Contains(m)) {
                        continue;
                    }

                    Node node = null;

                    try {
                    node = this.Transform(
                     m, roots[index], roots[index == 0 ? 1 : 0]);
                    this.Nodes[m] = node;
                    } catch (Exception ex) {
                        DefaultDependencyManager.Instance.Logger.WriteLog(ex.Message);
                    }
                }
            }
        }

        public Node Transform(string target, Node root) {
            FuncNode newRoot = new UserDefinedFunctionNode();

            return this.Transform(target, root, newRoot);
        }

        public Node Transform(string wanted, Node node, Node root) {
            FuncNode n = null;
            RouteFinder finder = new RouteFinder(wanted);

            if (finder.Visit(node)) {
                while (finder.Route.Count > 0) {
                    int direction = finder.Route.Pop();
                    if (node is IdentifierNode 
                        || 
                        node is IdentifierNode) {
                        break;
                    }

                    if (node is OperatorNode) {
                        OperatorNode operatorNode = (OperatorNode)node;
                        if (direction == 1
                        && (node is DivNode || node is SubNode || node is LogNode)) {
                            if (node.GetType().Equals(typeof(DivNode))) {
                                n = new DivNode();
                            } else if (node.GetType().Equals(typeof(SubNode))) {
                                n = new SubNode();
                            } else if (node is LogNode) {
                                n = new LogNode();
                            }
                            OperatorNode on = (OperatorNode)n;
                            on.Left = operatorNode.Left;
                            on.Right = root;

                            node = operatorNode.Right;
                        } else {
                            OperatorNode on
                                = (OperatorNode)operatorNode.GetReversedFunction(0);
                            on.Left = root;

                            if (direction == 0) {
                                on.Right
                                    = operatorNode.Right;
                                node = operatorNode.Left;
                            } else {
                                on.Right
                                    = operatorNode.Left;
                                node = operatorNode.Right;
                            }
                            n = on;
                        }
                    } else if (node is FuncNode) {
                        FuncNode fn = (FuncNode)node;
                        FuncNode nfn = fn.GetReversedFunction(direction);

                        if (fn.Parameters.Count > 0) {
                            int count = 0;
                            nfn.Parameters.Add(root);
                            for (int index = 0;
                                index < fn.Parameters.Count; index++) {
                                if (direction == index) {
                                    node = fn.Parameters[index];
                                    continue;
                                }

                                nfn.Parameters.Add(fn.Parameters[index]);
                                count++;
                            }
                        }
                    }
                    root = n;
                }
            }

            return root;
        }
    }
}
